{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "b952a1c0faad"
   },
   "outputs": [],
   "source": [
    "# @title Copyright 2022 The Cirq Developers\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "99918a03dd97"
   },
   "source": [
    "# Best practices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1cb18495690c"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://quantumai.google/cirq/google/best_practices\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/main/docs/google/best_practices.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/main/docs/google/best_practices.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/google/best_practices.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "d4c447ddd24e"
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    import cirq\n",
    "\n",
    "    print(\"installed cirq.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "3155099575b6"
   },
   "outputs": [],
   "source": [
    "import cirq_google as cg\n",
    "import sympy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "508c6853e5ad"
   },
   "source": [
    "This section lists some best practices for creating a circuit that performs well\n",
    "on Google hardware devices. This is an area of active research, so users are\n",
    "encouraged to try multiple approaches to improve results.\n",
    "\n",
    "This guide is split into three parts:\n",
    "*  Getting your circuit to run\n",
    "*  Making it run faster\n",
    "*  Lowering error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "a0d594142555"
   },
   "source": [
    "## Getting a circuit to run on hardware\n",
    "\n",
    "In order to run on hardware, the circuit must only use qubits and gates that the\n",
    "device supports.  Using inactive qubits, non-adjacent qubits, or non-native\n",
    "gates will immediately cause a circuit to fail.\n",
    "\n",
    "Validating a circuit with a device, such as\n",
    "`cg.Sycamore.validate_circuit(circuit)` will test a lot of these\n",
    "conditions.  Calling the `validate_circuit` function will work with any\n",
    "device, including those retrieved directly from the API using the\n",
    "[engine object](./specification.md#conversion-to-cirq.device), which can help\n",
    "identify any qubits used in the circuit that have been disabled on the actual\n",
    "device."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "35ed540d7845"
   },
   "source": [
    "### Using built-in transformers as a first pass\n",
    "\n",
    "Using built-in transformers will allow you to compile to the correct gate set. As they are\n",
    "automated solutions, they will not always perform as well as a hand-crafted solution, but\n",
    "they provide a good starting point for creating a circuit that is likely to run successfully\n",
    "on hardware. Best practice is to inspect the circuit after optimization to make sure\n",
    "that it has compiled without unintended consequences."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "47424abe7fa0"
   },
   "outputs": [],
   "source": [
    "# Create your circuit here\n",
    "my_circuit = cirq.Circuit()\n",
    "\n",
    "# Convert the circuit to run on a Google target gateset.\n",
    "# The google specific `cirq.CompilationTargetGateset` specifies the target gateset\n",
    "# and a sequence of appropriate optimization routines that should be executed to compile\n",
    "# a circuit to run on this target.\n",
    "sycamore_circuit = cirq.optimize_for_target_gateset(my_circuit, gateset=cg.SycamoreTargetGateset())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "682c487da95a"
   },
   "source": [
    "### Using CircuitOperation to reduce circuit size\n",
    "\n",
    "Particularly large batches (or sweeps) of circuits may encounter errors when\n",
    "sent to Quantum Engine due to an upper limit on request size. If the circuits\n",
    "in question have a repetitive structure, `cirq.CircuitOperation`s can be used\n",
    "to reduce the request size and avoid this limit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "87ce93d57c77"
   },
   "outputs": [],
   "source": [
    "# Repeatedly apply Hadamard and measurement to 10 qubits.\n",
    "my_circuit = cirq.Circuit()\n",
    "qubits = cirq.GridQubit.rect(2, 5)\n",
    "for i in range(100):\n",
    "    my_circuit.append(cirq.H.on_each(*qubits))\n",
    "    for qb in qubits:\n",
    "        my_circuit.append(cirq.measure(qb, key=cirq.MeasurementKey.parse_serialized(f'{i}:m{qb}')))\n",
    "\n",
    "print(my_circuit)\n",
    "\n",
    "# The same circuit, but defined using CircuitOperations.\n",
    "# This is ~1000x smaller when serialized!\n",
    "q = cirq.NamedQubit(\"q\")\n",
    "sub_circuit = cirq.FrozenCircuit(cirq.H(q), cirq.measure(q, key='m'))\n",
    "circuit_op = cirq.CircuitOperation(sub_circuit).repeat(100)\n",
    "short_circuit = cirq.Circuit(\n",
    "    circuit_op.with_qubits(q).with_measurement_key_mapping({'m': f'm{q}'}) for q in qubits\n",
    ")\n",
    "print(short_circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "d56256706648"
   },
   "source": [
    "When compiling circuits with `CircuitOperation`s, providing a context\n",
    "with `deep=True` will preserve the `CircuitOperation`s while\n",
    "optimizing their contents. This is useful for producing a concise,\n",
    "device-compatible circuit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "ab2ceb6235d0"
   },
   "outputs": [],
   "source": [
    "syc_circuit = cirq.optimize_for_target_gateset(\n",
    "    short_circuit, gateset=cg.SycamoreTargetGateset(), context=cirq.TransformerContext(deep=True)\n",
    ")\n",
    "print(syc_circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "e562f37f459b"
   },
   "source": [
    "## Running circuits faster\n",
    "\n",
    "The following sections give tips and tricks that allow you to improve your\n",
    "repetition rate (how many repetitions per second the device will run).\n",
    "\n",
    "This will allow you to make the most out of limited time on the\n",
    "device by getting results faster. The shorter experiment time may\n",
    "also reduce error due to drift of qubits away from calibration.\n",
    "\n",
    "There are costs to sending circuits over the network, to compiling each\n",
    "circuit into waveforms, to initializing the device,\n",
    "and to sending results back over the network.\n",
    "These tips will aid you in removing some of this overhead by combining your\n",
    "circuits into sweeps or batches."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0e58b5ded5cb"
   },
   "source": [
    "### Use sweeps when possible\n",
    "\n",
    "Round trip network time to and from the engine typically adds latency on the order of a second\n",
    "to the overall computation time.  Reducing the number of trips and allowing the engine to\n",
    "properly batch circuits can improve the throughput of your calculations.  One way to do this\n",
    "is to use parameter sweeps to send multiple variations of a circuit at once.\n",
    "\n",
    "One example is to turn single-qubit gates on or off by using parameter sweeps.\n",
    "For instance, the following code illustrates how to combine measuring in the\n",
    "Z basis or the X basis in one circuit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "48d80ab487d9"
   },
   "outputs": [],
   "source": [
    "q = cirq.GridQubit(1, 1)\n",
    "sampler = cirq.Simulator()\n",
    "\n",
    "# STRATEGY #1: Have a separate circuit and sample call for each basis.\n",
    "circuit_z = cirq.Circuit(cirq.measure(q, key='out'))\n",
    "circuit_x = cirq.Circuit(cirq.H(q), cirq.measure(q, key='out'))\n",
    "samples_z = sampler.sample(circuit_z, repetitions=5)\n",
    "samples_x = sampler.sample(circuit_x, repetitions=5)\n",
    "\n",
    "print(\"Measurement in Z Basis:\", samples_z, sep=\"\\n\")\n",
    "print(\"Measurement in X Basis:\", samples_x, sep=\"\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "c0c6a224bdd1"
   },
   "outputs": [],
   "source": [
    "# STRATEGY #2: Have a parameterized circuit.\n",
    "circuit_sweep = cirq.Circuit(cirq.H(q) ** sympy.Symbol('t'), cirq.measure(q, key='out'))\n",
    "\n",
    "samples_sweep = sampler.sample(circuit_sweep, repetitions=5, params=[{'t': 0}, {'t': 1}])\n",
    "print(samples_sweep)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "d55aa9866d01"
   },
   "source": [
    "One word of caution is there is a limit to the total number of repetitions.  Take some care\n",
    "that your parameter sweeps, especially products of sweeps, do not become so excessively large\n",
    "that they exceed this limit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "114fb6c6b766"
   },
   "source": [
    "### Use batches if sweeps are not possible\n",
    "\n",
    "The sampler has a method called `run_batch()` that can be used to send multiple\n",
    "circuits in a single request.  This can be used to increase the efficiency\n",
    "of your program so that more repetitions are completed per second.\n",
    "\n",
    "The circuits that are grouped into the same batch must\n",
    "measure the same qubits and have the same number of repetitions for each\n",
    "circuit.  Otherwise, the circuits will not be batched together\n",
    "on the device, and there will be no gain in efficiency."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ecf59af5793e"
   },
   "source": [
    "### Flatten sympy formulas into symbols\n",
    "\n",
    "Symbols are extremely useful for constructing parameterized circuits (see above).  However,\n",
    "only some sympy formulas can be serialized for network transport to the engine.\n",
    "Currently, sums and products of symbols, including linear combinations, are supported.\n",
    "See `cirq_google.arg_func_langs` for details.\n",
    "\n",
    "The sympy library is also infamous for being slow, so avoid using complicated formulas if you\n",
    "care about performance.  Avoid using parameter resolvers that have formulas in them.\n",
    "\n",
    "One way to eliminate formulas in your gates is to flatten your expressions.\n",
    "The following example shows how to take a gate with a formula and flatten it\n",
    "to a single symbol with the formula pre-computed for each value of the sweep:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "9bd97ca0fcfa"
   },
   "outputs": [],
   "source": [
    "# Suppose we have a gate with a complicated formula.  (e.g. \"2^t - 1\")\n",
    "# This formula cannot be serialized\n",
    "# It could potentially encounter sympy slowness.\n",
    "gate_with_formula = cirq.XPowGate(exponent=2 ** sympy.Symbol('t') - 1)\n",
    "sweep = cirq.Linspace('t', start=0, stop=1, length=5)\n",
    "\n",
    "# Instead of sweeping the formula, we will pre-compute the values of the formula\n",
    "# at every point and store it a new symbol called '<2**t - 1>'\n",
    "sweep_for_gate, flat_sweep = cirq.flatten_with_sweep(gate_with_formula, sweep)\n",
    "\n",
    "print(repr(sweep_for_gate))\n",
    "\n",
    "print(list(flat_sweep.param_tuples()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "c88809d4a998"
   },
   "source": [
    "## Improving circuit fidelity\n",
    "\n",
    "The following tips and tricks show how to modify your circuit to\n",
    "reduce error rates by following good circuit design principles that\n",
    "minimize the length of circuits.\n",
    "\n",
    "Quantum Engine will execute a circuit as faithfully as possible.\n",
    "This means that moment structure will be preserved. That is, all gates in a\n",
    "moment are guaranteed to be executed before those in any later moment and\n",
    "after gates in previous moments.  Many of these tips focus on having a\n",
    "good moment structure that avoids problematic missteps that can cause\n",
    "unwanted noise and error.\n",
    "\n",
    "Note: See the [Circuit optimization, gate alignment, and spin echoes tutorial](../tutorials/google/spin_echoes.ipynb) for an example of the best practices discussed in this section.\n",
    "\n",
    "### Short gate depth\n",
    "\n",
    "In the current NISQ (noisy intermediate-scale quantum) era, gates and devices still\n",
    "have significant error. Both gate errors and T1 decay rate can cause long circuits\n",
    "to have noise that overwhelms any signal in the circuit.\n",
    "\n",
    "The recommended gate depths vary significantly with the structure of the circuit itself\n",
    "and will likely increase as the devices improve. Total circuit fidelity can be roughly\n",
    "estimated by multiplying the fidelity for all gates in the circuit. For example,\n",
    "using a error rate of 0.5% per gate, a circuit of depth 20 and width 20 could be estimated\n",
    "at 0.995^(20 * 20) = 0.135. Using separate error rates per gates (i.e. based on calibration\n",
    "metrics) or a more complicated noise model can result in more accurate error estimation.\n",
    "\n",
    "### Terminal Measurements\n",
    "\n",
    "Make sure that measurements are kept in the same moment as the final moment in\n",
    "the circuit.  Make sure that any circuit optimizers do not alter this by\n",
    "incorrectly pushing measurements forward. This behavior can be avoided by\n",
    "measuring all qubits with a single gate or by adding\n",
    "the measurement gate after all optimizers have run.\n",
    "\n",
    "Currently, only terminal measurements are supported by the hardware.  If you\n",
    "absolutely need intermediate measurements for your application, reach out to\n",
    "your Google sponsor to see if they can help devise a proper circuit using\n",
    "intermediate measurements.\n",
    "\n",
    "\n",
    "### Keep qubits busy\n",
    "\n",
    "Qubits that remain idle for long periods tend to dephase and decohere. Inserting a\n",
    "[Spin Echo](https://en.wikipedia.org/wiki/Spin_echo) into your circuit onto\n",
    "qubits that have long idle periods, such as a pair\n",
    "of involutions, such as two successive Pauli Y gates, will generally increase\n",
    "performance of the circuit.\n",
    "\n",
    "Be aware that this should be done after calling\n",
    "`cirq.optimize_for_target_gateset`, since this function will 'optimize'\n",
    "these operations out of the circuit. You can also tag the spin echo operations with a no-compile tag, and include these tags in `context.tags_to_ignore`, so that the transformer ignores all tagged operations marked with any of `context.tags_to_ignore`. \n",
    "\n",
    "### Delay initialization of qubits\n",
    "\n",
    "The |0⟩ state is more robust than the |1⟩ state. As a result, one should\n",
    "not initialize a qubit to |1⟩ at the beginning of the circuit until shortly\n",
    "before other gates are applied to it.\n",
    "\n",
    "### Align single-qubit and two-qubit layers\n",
    "\n",
    "Devices are generally calibrated to circuits that alternate single-qubit gates with\n",
    "two-qubit gates in each layer. Staying close to this paradigm will often improve\n",
    "performance of circuits.  This will also reduce the circuit's total duration,\n",
    "since the duration of a moment is its longest gate.  Making sure that each layer\n",
    "contains similar gates of the same duration can be challenging, but it will\n",
    "likely have a measurable impact on the fidelity of your circuit.\n",
    "\n",
    "Devices generally operate in the Z basis, so that rotations around the Z axis will become\n",
    "book-keeping measures rather than physical operations on the device. These\n",
    "virtual Z operations have zero duration and have no cost, if they add no moments\n",
    "to your circuit.  In order to guarantee that they do not add moments, you can\n",
    "make sure that virtual Z are aggregated into their own layer.  Alternatively,\n",
    "you can use the `cirq.eject_z` optimizer to propagate these Z gates forward through\n",
    "commuting operators.\n",
    "\n",
    "See the function `cirq.stratified_circuit` for an automated way to organize gates\n",
    "into moments with similar gates.\n",
    "\n",
    "### Qubit picking\n",
    "\n",
    "On current NISQ devices, qubits cannot be considered identical.  Different\n",
    "qubits can have vastly different performance and can vary greatly from day\n",
    "to day.  It is important for experiments to have a dynamic method to\n",
    "pick well-performing qubits that maximize the fidelity of the experiment.\n",
    "There are several techniques that can assist with this.\n",
    "\n",
    "*   Analyze calibration metrics:  performance of readout, single-qubit, and\n",
    "two-qubit gates are measured as a side effect of running the device's\n",
    "calibration procedure.  These metrics can be used as a baseline to evaluate\n",
    "circuit performance or identify outliers to avoid.  This data can be inspected\n",
    "programmatically by retrieving metrics from the [API](calibration.md) or\n",
    "[visually by applying a cirq.Heatmap](../tutorials/google/visualizing_calibration_metrics.ipynb)\n",
    "to that data or by using the built-in\n",
    "heatmaps in the Cloud console page for the processor.  Note that, since this\n",
    "data is only taken during calibration (e.g. at most daily), drifts and other\n",
    "concerns may affect the values significantly, so these metrics should only be\n",
    "used as a first approximation.  There is no substitute for actually running characterizations\n",
    "on the device.\n",
    "*   Loschmidt echo:  Running a small circuit on a string of qubits and then\n",
    "applying the circuit's inverse can be used as a quick but effective way to\n",
    "judge qubit quality.  See\n",
    "[this tutorial](../tutorials/google/echoes.ipynb) for instructions.\n",
    "*   XEB:  Cross-entropy benchmarking is another way to gauge qubit performance\n",
    "on a set of random circuits.  See tutorials on\n",
    "[parallel XEB](../noise/qcvv/parallel_xeb.ipynb)\n",
    "or [isolated XEB](../noise/qcvv/isolated_xeb.ipynb) for instructions.\n",
    "\n",
    "\n",
    "### Refitting gates\n",
    "\n",
    "Virtual Z gates (or even single qubit gates) can be added to adjust for errors\n",
    "in two qubit gates.  Two qubit gates can have errors due to drift, coherent\n",
    "error, unintended cross-talk, or other sources.  Refitting these gates and\n",
    "adjusting the circuit for the observed unitary of the two qubit gate\n",
    "compared to the ideal unitary can substantially improve results.\n",
    "However, this approach can use a substantial amount of resources.\n",
    "\n",
    "This technique involves two distinct steps.  The first is *characterization*,\n",
    "which is to identify the true behavior of the two-qubit gate.  This typically\n",
    "involves running many varied circuits involving the two qubit gate in a method\n",
    "(either periodic or random) to identify the parameters of the gate's behavior.\n",
    "\n",
    "Entangling gates used in Google's architecture fall into a general category of FSim gates,\n",
    "standing for *Fermionic simulation*.  The generalized version of this gate can\n",
    "be parameterized into 5 angles, or degrees of freedom.  Characterization will\n",
    "attempt to identify the values of these five angles.\n",
    "\n",
    "The second step is calibrating (or refitting) the gate.  Out of the five angles\n",
    "that comprise the generalized FSim gate, three can be corrected for by adding\n",
    "Z rotations before or after the gate.  Since these gates are propagated forward\n",
    "automatically, they add no duration or error to the circuit and can essentially\n",
    "be added \"for free\".  See the [devices page](devices.md#virtual_z_gates) for more\n",
    "information on Virtual Z gates.  Note that it is important to keep the single-qubit and\n",
    "two-qubit gates aligned (see above) while performing this procedure so that\n",
    "the circuit stays the same duration.\n",
    "\n",
    "For more on calibration and detailed instructions on how to perform these procedures, see the following tutorials:\n",
    "\n",
    "* [XEB calibration theory](../noise/qcvv/xeb_theory.ipynb)\n",
    "* [Floquet calibration](https://www.youtube.com/watch?v=hYDWOz1r2Ys&t=243s)"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "best_practices.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
